#pragma once

/**
 *
 */

// socket bind accept listen connect
// server:  socket bind listen accept
// client:  socket connect

#include <arpa/inet.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <sys/socket.h>

namespace devem {

static const std::string defaultServerIP = "127.0.0.1";
static const uint16_t defaultPort = 8080;
static const int defaultSock = -1;
static const int defaultBacklog = 5;

class Socket {
public:
    Socket() {}
    Socket(int sock) : sock_(sock) {}

    int Fd() { return sock_; }

    bool Create();

    bool Bind(const uint16_t& server_port = defaultPort);

    int Accept(std::string* client_ip, uint16_t* client_port);

    bool Listen(int backlog = defaultBacklog);

    bool Connect(const std::string& server_ip = defaultServerIP, const uint16_t& server_port = defaultPort);

    // 建立一个服务端 / 客户端
    bool CreateServer() {
        if (!Create() || !Bind() || !Listen()) {
            return false;
        }
        return true;
    }

    bool CreateClient() {
        Create();
        Connect();
    }

    // 读 / 写
    ssize_t Socket::Recv(void* buf, int len, int flag = 0);
    ssize_t Socket::Send(void* buf, int len, int flag = 0);

    // 设置套接字为非阻塞
    bool SetNonBlock(int sock) {
        int fd = fcntl(sock, F_GETFL);
        if (fd < 0)
            return false;
        fcntl(sock, F_SETFL, fd | O_NONBLOCK);
        return true;
    }

    // 支持端口复用
    void ReuseAddress() {
        // 第二个参数表示选项定义的层次，SOL_SOCKET 表示基本套接口选项
        // 第三个参数是需要设置的选项名，SO_REUSEADDR 表示复用 ip 地址和端口，SO_REUSEPORT 可要可不要
        // 第四个参数指向变量或结构的指针，包含选项的新值
        // 第五个参数是变量的大小
        int opt = 1;
        setsockopt(sock_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    }

    void Close() {
        if (sock_ >= 0) {
            close(sock_);
            sock_ = defaultSock;
        }
    }

    ~Socket() { Close(); }

private:
    int sock_;
};
}  // namespace devem